import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Container;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

/**
 * When instantiated, will run the game (using run() method).
 */
public class Game
{
	
	//INSTANCE VARIABLES
	
	/**
	 * Number of party members *including* the party leader.
	 */
	private final int PARTY_SIZE = 5;
	
	/**
	 * Each successive pace setting increases the amount of miles traveled 
	 * per day by this amount. ie. Mult = 7: Leisurely = 7, Steady = 14,
	 * and Grueling = 21.
	 */
	private final int PACE_MULTIPLIER = 5;
	
	/**
	 * First member in array is the leader, others are party members.
	 */
	private PartyMember[] party;
	
	/**
	 * Including separate reference to leader for method brevity/clarity later on.
	 */
	private PartyLeader leader;
	
	private Wagon wagon;
	private Equipment eq;
	private Map map;
	private Store store;
	
	private JFrame GameWindow;

	/**
	 * The container panel that holds all of the other UI panels.
	 */
	private JPanel UIPanels;
	
	//All of the UI Screens in the game (so far).
	private MainMenuPanel mmpanel;
	private ConfigurationPanel cfgpanel;
	private StorePanel storepanel;
	private TurnPanel turnpanel;
	private PaceRationsPanel prpanel;
	private CreditPanel crPanel;
	private MapPanel mPanel;
	
	//Variables used in logic for updating the store displayed in the game.
	private boolean newStore;
	private int storenum;
	
	
	
	
    /**
     * Starts the game session.
     */
	public void run()
	{
		runGame();
	} //end run
	
    /**
     * Initializes instance variables and calls the method to create the GUI.
     */
	private void runGame()
	{
		wagon = new Wagon(0, 0);
		eq = new Equipment();
		map = new Map();
		store = new Store(0);
		newStore = true;
		storenum = 0;
		createUI();
		
	} //end runGame
	
	/**
	 * Creates the user interface. Makes a JFrame, adds all of the UI screens
	 * to it, and makes it visible.
	 */
	public void createUI() {
		
		//Create and set up the Game window
		GameWindow = new JFrame("Oregon Trail w/ Zombies");
		GameWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		GameWindow.setBounds(100, 100, 800, 600);
		
		addPanelsToFrame(GameWindow.getContentPane());
		
		GameWindow.setVisible(true);
	}//end createUI

	
	public void addPanelsToFrame(Container pane){
		
		//The holder panel for the individual UI panels. Uses CardLayout
		UIPanels = new JPanel(new CardLayout());
		
		//Create all of the the UI Panels
		mmpanel = new MainMenuPanel(this);
		cfgpanel = new ConfigurationPanel(this);
		//FIXME storepanel (ui class) shouldn't have access to store (game class)
		storepanel = new StorePanel(store, this);
		turnpanel = new TurnPanel(this);
		prpanel = new PaceRationsPanel(this);
		crPanel = new CreditPanel();
		mPanel = new MapPanel(this, "/resources/us_outline.jpg");
		
		//Add each of the UI screens individually to the set of UIPanels
		UIPanels.add(mmpanel, "mmpanel");
		UIPanels.add(cfgpanel, "cfgpanel");
		UIPanels.add(storepanel, "storepanel");
		UIPanels.add(turnpanel, "turnpanel");
		UIPanels.add(prpanel, "prpanel");
		UIPanels.add(crPanel, "CreditPanel");
		UIPanels.add(mPanel, "MapPanel");
		
		
		//Add the set of UIPanels to the container
		pane.add(UIPanels, BorderLayout.CENTER);
	}//end addPanelsToFrame
	
	
	//FIXME change input param to enum accessible by all UI panels
	/**
	 * Tells the Game Window which UI panel to display.
	 * @param newPanel - the panel to display.
	 */
	public void changePanel(String newPanel){
		CardLayout cl = (CardLayout) (UIPanels.getLayout());
		
		if (newPanel.equals("ConfigurationPanel")){
			cl.show(UIPanels, "cfgpanel");
		}
		
		else if (newPanel.equals("StorePanel"))
		{
			cl.show(UIPanels, "storepanel");
		}
		
		else if (newPanel.equals("TurnPanel")){
			int food = eq.getQuantities()[1];
			try
			{
				turnpanel.updatePanel(map.getLocation(), food, Map.Landmark_Locations[map.getNextLandmarkIndex()] == map.getToNextLandmark() && Map.Landmarks[(map.getNextLandmarkIndex() - 1)].hasStore());
			}
			catch(ArrayIndexOutOfBoundsException e)
			{
				turnpanel.updatePanel(map.getLocation(), food, true);
			}
			cl.show(UIPanels, "turnpanel");
		}
		else if (newPanel.equals("PaceRationsPanel")){
			int paceIndex = (wagon.getPace() / PACE_MULTIPLIER);
			prpanel.updatePanel(paceIndex, wagon.getRations());
			cl.show(UIPanels, "prpanel");
		}
		else if(newPanel.equals("CreditPanel"))
		{
			cl.show(UIPanels,  "CreditPanel");
		}
		else if(newPanel.equals("MapPanel"))
		{
			cl.show(UIPanels, "MapPanel");
		}
	}//end changePanel
	
	
	/**
	 * Configures the party's names, leader's profession, and initial pace
	 * and ration settings after the configuration screen is completed.
	 */
	public void configuration(){
		String[] names = cfgpanel.getNames();
		int paceIndex = cfgpanel.getPaceIndex();
		int rationsIndex = cfgpanel.getRationsIndex();
		Profession prof = cfgpanel.getProfession();
		
		party = new PartyMember[PARTY_SIZE];
		party[0] = new PartyLeader(names[0], prof);
		leader = (PartyLeader) party[0];
		
		for (int i = 1; i < PARTY_SIZE; i++)
		{
			party[i] = new PartyMember(names[i]);
		}
		
		setPaceRations(paceIndex, rationsIndex);
	}//end configuration

	
	/**
	 * Attempts to complete a transaction.
	 * 
	 * @param quantities - The quantities of each item (in same order as in
	 * 						the Equipment class) that the user wants to buy.
	 * @return true if purchase was successful, false otherwise
	 */
	public boolean attemptPurchase(int[] quantities){
		
		int[] quants = quantities;
		
		int cost = calcTotalCost(quants, store);
		int weight = calcTotalWeight(quants);

		//If any of the requested quantities are negative, negAmounts = true
		boolean negAmounts = false;
		for (int i=0; i<quants.length; i++){
			if (quants[i] < 0) {
				negAmounts = true;
			}
		}
		
		if(negAmounts){
			//At least one requested quantity was negative.
			//FIXME dialog box instead
			System.out.println("\"Bizarro want negative items\"? I've got a business to run, kid.");
			return false;
		}
		
		if(cost > leader.getMoney()) {
			//Total cost of requested items exceeds available money.
			//FIXME dialog box instead
			System.out.println("This stuff ain't free, pal.");
			return false;
		}
		if (weight > (Equipment.MAX_WEIGHT - eq.getWeight())) {
			//Total weight of requested items exceeds available space on wagon.
			//FIXME dialog box instead
			System.out.println("If you don't have room for it, don't buy it.");
			return false;
		}
		if(!store.removeItems(quants))
		{
			System.out.println("We don't have that many!");
			return false;
		}

		else {
			//Everything checks out -- purchase successful.
			leader.addMoney(-cost);
			eq.addItems(quants);
			return true;
		}
	}//end attemptPurchase
	
	/**
	 * Method called when "Move" button is clicked. Moves the wagon, subtracts
	 * consumed food from equipment, updates the turn screen with new dist &
	 * remaining food.
	 */
	public void move(){
		
		map.move(wagon.getPace());
		
		int foodConsumed = (wagon.getRations()) * (getPartyNumber());
		int[] foodArray = new int[2];
		foodArray[1] = (0 - foodConsumed);
		eq.addItems(foodArray);
				
		int foodRemaining = eq.getQuantities()[1];
		
		if(Map.Landmark_Locations[map.getNextLandmarkIndex()] == map.getToNextLandmark() && map.getNextLandmarkIndex() != 0)
		{	
			JOptionPane.showMessageDialog(GameWindow, "You've reached " + Map.Landmarks[map.getNextLandmarkIndex()-1].getName(), "Congratulations!", JOptionPane.INFORMATION_MESSAGE);
		}
		
		turnpanel.updatePanel(map.getLocation(), foodRemaining, Map.Landmark_Locations[map.getNextLandmarkIndex()] == map.getToNextLandmark() && Map.Landmarks[(map.getNextLandmarkIndex() - 1)].hasStore());
		mPanel.update(Map.Landmark_Locations[map.getNextLandmarkIndex()] == map.getToNextLandmark());
		
		if(Map.Landmark_Locations[map.getNextLandmarkIndex()] == map.getToNextLandmark() && Map.Landmarks[(map.getNextLandmarkIndex() - 1)].hasStore() && newStore) // arrived at landmark with a store, so update store variable
		{
			store = new Store(++storenum);
			storepanel = new StorePanel(store, this);
			UIPanels.remove(storepanel);
			UIPanels.add(storepanel, "storepanel");
			newStore = false;
		}
		else
			newStore = true;
		
		
		if(map.thereYet()) //We have reached Oregon!
			changePanel("CreditPanel");
	}
	
	//TODO make separate party class which holds party members and move this function to it?
	/**
	 * Returns the number of non-dead party members, including the leader.
	 * 
	 * @return - Number of living party members.
	 */
	private int getPartyNumber(){
		
		int partynum = 0;
		for (int i = 0; i < party.length; i++){
			if (party[i].getStatus() != 2){
				partynum++;
			}
		}
		return partynum;
	}
	
	//TODO move calculations to Store class
	/**
	 * Helper function that calculates the cost of an order on store screen.
	 * @param a List of the desired quantities of items
	 * @param s The store the items are being purchased from
	 * @return Total cost of the items
	 */
	private int calcTotalCost(int[] a, Store s)
	{
		int cost = 0;
		for(int i = 0; i < a.length; i++)
			cost += a[i] * (s.getCosts())[i];
		
		return cost;
	}
	
	//TODO move calculations to store class
	/**
	 * Helper function that calculates the weight of an order on store screen
	 * @param a List of the desired quantities of items
	 * @return Weight of the desired items
	 */
	private int calcTotalWeight(int[] a)
	{
		int weight = 0;
		for(int i = 0; i < a.length; i++)
			weight += a[i] * (Store.Item_List)[i].getWeight();
		
		return weight;
	}
	
	/**
	 * Returns the amount of remaining food in pounds.
	 * 
	 * @return - remaining food.
	 */
	public int foodLeft() {
		return eq.getQuantities()[1];
	}
	
	/**
	 * Changes the pace and rations.
	 * 
	 * @param paceIndex - Stopped = 0, Leisurely = 1, Steady = 2, Grueling = 3.
	 * @param rationsIndex - Food per person per day.
	 */
	public void setPaceRations(int paceIndex, int rationsIndex){
		wagon.setPace(paceIndex * PACE_MULTIPLIER);
		wagon.setRations(rationsIndex);
	}
	
	/**
	 * main method
	 * 
	 * @param args
	 */
	public static void main(String[] args)
	{
		Game game = new Game();
		game.run();
	}

}
